import re
from find_title_line import FindTitleLine


class EditToc:
    def __init__(self, depth_from: int = 1, depth_to: int = 6):
        self.depth_from = depth_from
        self.depth_to = depth_to
        self.r_title_content = re.compile(r"#+(?P<title>.+)")
        self.setting_pattern = re.compile(
            r'<!-- @import "\[TOC\]" {cmd="toc" depthFrom=(?P<depth_from>\d+)\s+depthTo=(?P<depth_to>\d+)\s*} -->')
        self.toc_start_pattern = re.compile(r"<!-- code_chunk_output -->\s*")
        self.toc_end_pattern = re.compile(r"<!-- /code_chunk_output -->\s*")

    def toc_setting(self):
        return f'<!-- @import "[TOC]" {{cmd="toc" depthFrom={self.depth_from} depthTo={self.depth_to}}} -->\n'

    def toc_start_sign(self):
        return '<!-- code_chunk_output -->\n'

    def toc_end_sign(self):
        return '<!-- /code_chunk_output -->\n'

    def get_title_ref_GFM(self, title_content):
        """
        规则：小圆点省略，空格换成-
        """
        ref = '#' + title_content.lower().replace(' ', '-').replace('.',
                                                                    '').replace('`', '').replace('&', '')
        return ref

    def get_toc_from_title_lines(self, title_lines, levels):
        def indent(title_level):
            return ' ' * (title_level-2) * 2

        toc = []
        for level, line in zip(levels, title_lines):
            if level < self.depth_from or level > self.depth_to:
                continue
            title_content = self.r_title_content.search(
                line).group("title").strip()
            title = title_content  # 标题行中 ## 标志后的内容经过 strip()
            ref = self.get_title_ref_GFM(title_content)
            toc.append(f"{indent(level)}- [{title}]({ref})\n")
        return toc

    def find_toc_setting(self, lines):
        """
        遍历每一行，找到toc的三个标识行，并记录行号
        """
        self.setting_line_index = -1
        self.toc_start_index = -1
        self.toc_end_index = -1
        for i, line in enumerate(lines):
            tmp = self.setting_pattern.search(line)
            if tmp is not None:
                self.setting_line_index = i
                setting = tmp.groupdict()
                self.depth_from = int(setting["depth_from"].strip())
                self.depth_to = int(setting["depth_to"].strip())
                continue
            tmp = self.toc_start_pattern.search(line)
            if tmp is not None:
                self.toc_start_index = i
                continue
            tmp = self.toc_end_pattern.search(line)
            if tmp is not None:
                self.toc_end_index = i
                break

    def get_toc(self, lines: list):
        f = FindTitleLine()
        levels, title_lines = [], []
        for line in lines:
            level = f(line)
            if level > 0:
                levels.append(level)
                title_lines.append(line)
        toc = self.get_toc_from_title_lines(title_lines, levels)
        return toc

    def insert_toc_to_lines(self, toc: list, lines: list):
        if -1 < self.toc_start_index <= self.toc_end_index:

            # 插入新toc
            toc = ["\n"] + toc + ["\n"]
            if isinstance(lines, list):
                del lines[self.toc_start_index+1: self.toc_end_index]
                for toc_i in toc[::-1]:
                    lines.insert(self.toc_start_index+1, toc_i)
            else:
                del lines[self.toc_start_index: self.toc_end_index-1]
                lines.append(toc, self.toc_start_index)
            return lines

    def insert_toc(self, lines, index):
        """插入目录

        Args:
            lines (list): markdown文档的所有行
            index (int): 插入目录的位置

        Returns:
            list: 插入了目录的markdown所有行
        """

        index = index if index < len(lines) else len(lines) - 1
        if isinstance(lines, list):
            lines.insert(index-1, "\n")
            lines.insert(index, self.toc_setting())
            lines.insert(index+1, "\n")
            lines.insert(index+2, self.toc_start_sign())
            lines.insert(index+3, self.toc_end_sign())
            lines.insert(index+4, "\n")
            self.toc_start_index = index+2
            self.toc_end_index = index+3
        else:
            lines.append("\n", index-1)
            lines.append(self.toc_setting(), index)
            lines.append("\n", index+1)
            lines.append(self.toc_start_sign(), index+2)
            lines.append(self.toc_end_sign(), index+3)
            lines.append("\n", index+4)
            self.toc_start_index = index+3
            self.toc_end_index = index+4
        toc = self.get_toc(lines)
        self.insert_toc_to_lines(toc, lines)
        return lines

    def update_toc(self, lines):
        """刷新目录

        Args:
            lines (list): markdown 文档的所有行
        """

        self.find_toc_setting(lines)
        if self.setting_line_index == -1:
            raise TypeError("找不到 toc 的配置行, 需要先插入目录")
        if self.toc_start_index == -1 or self.toc_end_index == -1:
            raise TypeError("找不到 toc 的起始或结束标志行, 需要先插入目录")

        if not isinstance(lines, list):  # 用于vim， type(lines) == vim.Buffer
            self.toc_start_index += 1  # vim 从1开始
            self.toc_end_index += 1

        toc = self.get_toc(lines)

        self.insert_toc_to_lines(toc, lines)
        return lines


if __name__ == "__main__":
    path = "example.md"
    with open(path, encoding="utf8") as fr:
        lines_ = fr.readlines()
    toc_tools = EditToc()
    new_lines = toc_tools.insert_toc(lines_, 0)
    # new_lines = toc_tools.update_toc(lines_)

    lines_ = [i+"\n" for i in lines_]
    with open("aaa.md", 'w', encoding='utf8') as fw:
        fw.writelines(new_lines)
